/*
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package commandhandlers;

import input.IRCCommand;
import interfaces.CommandHandler;
import interfaces.TextHandler;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import main.Bot;
import main.CommonMethods;
import main.Constants;
import main.Main;
import main.UserCentral;

import org.apache.log4j.Logger;
import org.jibble.pircbot.Colors;

import output.IRCOutput;
import timers.NewsTimer;
import tools.InputType;
import tools.StringTools;
import tools.UtopiaTime;
import botobjects.NewsItem;
import botobjects.Summary;
import database.DBConnectionFactory;
import exceptions.UtoDateException;

/**
 * This class handles commands related to utopia news
 * 
 * 
 */
public class NewsHandler extends CommandHandler implements TextHandler {
   private static NewsHandler INSTANCE = null;

   /**
    * Returns information about when the last war with the specified kd started or ended
    * 
    * @param kd
    * @param warStart
    * @return
    */
   public static String getLastWarInfo(final String kd, final boolean warStart) {
	  String out = "";
	  PreparedStatement ps = null;
	  ResultSet rs = null;
	  try {
		 if (warStart) {
			ps = DBConnectionFactory
				  .getConnectionHandler()
				  .getConnection()
				  .prepareStatement(
						"SELECT utodate FROM " + "news WHERE " + "newsitem LIKE ? OR newsitem LIKE ? ORDER BY realtime DESC");
			ps.setString(1, "%We have declared WAR on %" + kd + ("".equals(kd) ? "" : "%"));
			ps.setString(2, "%" + kd + " has declared WAR with our kingdom%");
		 } else {
			ps = DBConnectionFactory
				  .getConnectionHandler()
				  .getConnection()
				  .prepareStatement(
						"SELECT utodate FROM news WHERE "
							  + "newsitem LIKE ? OR newsitem LIKE ? OR newsitem LIKE ? OR newsitem LIKE ? ORDER BY realtime DESC");
			ps.setString(1, "%withdrawn from war with %" + kd + ("".equals(kd) ? "" : "%"));
			ps.setString(2, "%" + kd + " has withdrawn from war%");
			ps.setString(3, "%We have accepted an offer of Peace by %" + kd + ("".equals(kd) ? "" : "%"));
			ps.setString(4, "%" + kd + " has accepted our offer of Peace%");
		 }
		 rs = ps.executeQuery();

		 if (rs.next())
			return rs.getString("utodate");
	  } catch (final SQLException e) {
		 Logger.getLogger("Error").error("", e);
	  } finally {
		 try {
			rs.close();
		 } catch (final Exception e) {
		 }
		 try {
			ps.close();
		 } catch (final Exception e) {
		 }
	  }
	  return out;
   }

   /**
    * Returns a summary based on the supplied newslist
    * 
    * @param war
    *           Decides whether the bot should only care about news from a war or all of the news
    * @param news
    *           List of news to be summarized
    * @param kdTarget
    *           Decides whether the bot should focus on news regarding a specific kd or not
    * @return
    */
   public static Summary getSummary(final boolean war, final List<NewsItem> news, final String kdTarget) {
	  boolean warStarted = false;
	  Summary out = new Summary();
	  String kdMatch = KDREGEX;
	  if (StringTools.validateInput(InputType.KDLOC, kdTarget)) {
		 kdMatch = kdTarget.replaceAll("[^0-9:]", "");
		 kdMatch = "\\((" + kdMatch + ")\\)";
	  }
	  try {
		 String ownProvinceMatch = ".*" + "\\((" + Main.getBotSettings().getKdLoc() + ")\\)";
		 String enemyProvinceMatch = ".*" + kdMatch;
		 for (NewsItem item : news) {
			if (war && !warStarted) {
			   // only want the items from the war
			   if (item.news.matches(".*We have declared WAR on .*" + kdMatch + "![ ]{0,}")
					 || item.news.matches(".*" + kdMatch + " has declared WAR with our kingdom![ ]{0,}")) {
				  warStarted = true;
			   }
			} else {
			   if (item.news.matches(ownProvinceMatch + " captured " + AMOUNTREGEX + " acres of land from " + enemyProvinceMatch
					 + "\\.[ ]{0,}")) {
				  String newsItem = item.news;
				  String attacker = newsItem.substring(0, newsItem.indexOf('(')).trim();
				  int gain = Integer.parseInt(newsItem.substring(newsItem.indexOf("captured"), newsItem.indexOf("acres"))
						.replaceAll("[^0-9]", ""));
				  String target = newsItem.substring(newsItem.indexOf("from ") + 5).trim();
				  target = target.substring(0, target.indexOf('(')).trim();
				  out.addSelfHit("TM", attacker, target, gain, item.amount, warStarted);
			   } else if (item.news.matches(enemyProvinceMatch + " invaded " + ownProvinceMatch + " and captured " + AMOUNTREGEX
					 + " acres of land\\.[ ]{0,}")) {
				  String newsItem = item.news;
				  String attacker = newsItem.substring(0, newsItem.indexOf('(')).trim();
				  int gain = Integer.parseInt(newsItem.substring(newsItem.indexOf("and captured"), newsItem.indexOf("acres"))
						.replaceAll("[^0-9]", ""));
				  String target = newsItem.substring(newsItem.indexOf("invaded ") + 8).trim();
				  target = target.substring(0, target.indexOf('(')).trim();
				  out.addEnemyHit("TM", attacker, target, gain, item.amount, warStarted);
			   } else if (item.news.matches(ownProvinceMatch + ", captured " + AMOUNTREGEX + " acres of land from "
					 + enemyProvinceMatch + "\\.[ ]{0,}")) {
				  String newsItem = item.news;
				  String attacker = newsItem.substring(0, newsItem.indexOf('(')).trim();
				  int gain = Integer.parseInt(newsItem.substring(newsItem.indexOf("captured"), newsItem.indexOf("acres"))
						.replaceAll("[^0-9]", ""));
				  String target = newsItem.substring(newsItem.indexOf("from ") + 5).trim();
				  target = target.substring(0, target.indexOf('(')).trim();
				  out.addSelfHit("Conquest", attacker, target, gain, item.amount, warStarted);
			   } else if (item.news.matches(ownProvinceMatch + " invaded and pillaged " + enemyProvinceMatch + "\\.[ ]{0,}")) {
				  String newsItem = item.news;
				  String attacker = newsItem.substring(0, newsItem.indexOf('(')).trim();
				  int gain = 0;
				  String target = newsItem.substring(newsItem.indexOf("pillaged ") + 9).trim();
				  target = target.substring(0, target.indexOf('(')).trim();
				  out.addSelfHit("Plunder", attacker, target, gain, item.amount, warStarted);
			   } else if (item.news.matches(enemyProvinceMatch + " attacked and pillaged the lands of " + ownProvinceMatch
					 + "\\.[ ]{0,}")) {
				  String newsItem = item.news;
				  String attacker = newsItem.substring(0, newsItem.indexOf('(')).trim();
				  int gain = 0;
				  String target = newsItem.substring(newsItem.indexOf("lands of ") + 9).trim();
				  target = target.substring(0, target.indexOf('(')).trim();
				  out.addEnemyHit("Plunder", attacker, target, gain, item.amount, warStarted);
			   } else if (item.news.matches(ownProvinceMatch + " invaded and stole from " + enemyProvinceMatch + "\\.[ ]{0,}")) {
				  String newsItem = item.news;
				  String attacker = newsItem.substring(0, newsItem.indexOf('(')).trim();
				  int gain = 0;
				  String target = newsItem.substring(newsItem.indexOf("stole from ") + 11).trim();
				  target = target.substring(0, target.indexOf('(')).trim();
				  out.addSelfHit("Learn", attacker, target, gain, item.amount, warStarted);
			   } else if (item.news.matches(enemyProvinceMatch + " attacked and stole from " + ownProvinceMatch + "\\.[ ]{0,}")) {
				  String newsItem = item.news;
				  String attacker = newsItem.substring(0, newsItem.indexOf('(')).trim();
				  int gain = 0;
				  String target = newsItem.substring(newsItem.indexOf("stole from ") + 11).trim();
				  target = target.substring(0, target.indexOf('(')).trim();
				  out.addEnemyHit("Learn", attacker, target, gain, item.amount, warStarted);
			   } else if (item.news.matches(ownProvinceMatch + " attempted an invasion of " + enemyProvinceMatch
					 + ", but was repelled\\.[ ]{0,}")) {
				  String newsItem = item.news;
				  String attacker = newsItem.substring(0, newsItem.indexOf('(')).trim();
				  int gain = 0;
				  String target = newsItem.substring(newsItem.indexOf("invasion of ") + 12).trim();
				  target = target.substring(0, target.indexOf('(')).trim();
				  out.addSelfHit("Bounce", attacker, target, gain, item.amount, warStarted);
			   } else if (item.news.matches(enemyProvinceMatch + " attempted to invade " + ownProvinceMatch + "\\.[ ]{0,}")) {
				  String newsItem = item.news;
				  String attacker = newsItem.substring(0, newsItem.indexOf('(')).trim();
				  int gain = 0;
				  String target = newsItem.substring(newsItem.indexOf("invade ") + 7).trim();
				  target = target.substring(0, target.indexOf('(')).trim();
				  out.addEnemyHit("Bounce", attacker, target, gain, item.amount, warStarted);
			   } else if (item.news.matches(ownProvinceMatch + " recaptured " + AMOUNTREGEX + " acres of land from "
					 + enemyProvinceMatch + "\\.[ ]{0,}")) {
				  String newsItem = item.news;
				  String attacker = newsItem.substring(0, newsItem.indexOf('(')).trim();
				  int gain = Integer.parseInt(newsItem.substring(newsItem.indexOf("captured"), newsItem.indexOf("acres"))
						.replaceAll("[^0-9]", ""));
				  String target = newsItem.substring(newsItem.indexOf("from ") + 5).trim();
				  target = target.substring(0, target.indexOf('(')).trim();
				  out.addSelfHit("Ambush", attacker, target, gain, item.amount, warStarted);
			   } else if (item.news.matches(enemyProvinceMatch + " ambushed armies from " + ownProvinceMatch + " and took "
					 + AMOUNTREGEX + " acres of land\\.[ ]{0,}")) {
				  String newsItem = item.news;
				  String attacker = newsItem.substring(0, newsItem.indexOf('(')).trim();
				  String target = newsItem.substring(newsItem.indexOf("armies from ") + 12).trim();
				  target = target.substring(0, target.indexOf('(')).trim();
				  int gain = Integer.parseInt(newsItem.substring(newsItem.indexOf("and took"), newsItem.indexOf("acres"))
						.replaceAll("[^0-9]", ""));
				  out.addEnemyHit("Ambush", attacker, target, gain, item.amount, warStarted);
			   } else if (item.news.matches(ownProvinceMatch + " killed " + AMOUNTREGEX + " people within " + enemyProvinceMatch
					 + "\\.[ ]{0,}")) {
				  String newsItem = item.news;
				  String attacker = newsItem.substring(0, newsItem.indexOf('(')).trim();
				  int gain = 0;
				  String target = newsItem.substring(newsItem.indexOf("people within ") + 14).trim();
				  target = target.substring(0, target.indexOf('(')).trim();
				  out.addSelfHit("Massacre", attacker, target, gain, item.amount, warStarted);
			   } else if (item.news.matches(enemyProvinceMatch + " invaded " + ownProvinceMatch + " and killed " + AMOUNTREGEX
					 + " people\\.[ ]{0,}")) {
				  String newsItem = item.news;
				  String attacker = newsItem.substring(0, newsItem.indexOf('(')).trim();
				  int gain = 0;
				  String target = newsItem.substring(newsItem.indexOf("invaded ") + 8).trim();
				  target = target.substring(0, target.indexOf('(')).trim();
				  out.addEnemyHit("Massacre", attacker, target, gain, item.amount, warStarted);
			   } else if (item.news.matches(ownProvinceMatch + " invaded " + enemyProvinceMatch + " and razed " + AMOUNTREGEX
					 + " acres of land\\.[ ]{0,}")) {
				  String newsItem = item.news;
				  String attacker = newsItem.substring(0, newsItem.indexOf('(')).trim();
				  int gain = Integer.parseInt(newsItem.substring(newsItem.indexOf("and razed"), newsItem.indexOf("acres"))
						.replaceAll("[^0-9]", ""));
				  String target = newsItem.substring(newsItem.indexOf("invaded ") + 8).trim();
				  target = target.substring(0, target.indexOf('(')).trim();
				  out.addSelfHit("Raze", attacker, target, warStarted ? 0 : gain, item.amount, warStarted);
			   } else if (item.news.matches(enemyProvinceMatch + " razed " + AMOUNTREGEX + " acres of " + ownProvinceMatch
					 + "\\.[ ]{0,}")) {
				  String newsItem = item.news;
				  String attacker = newsItem.substring(0, newsItem.indexOf('(')).trim();
				  int gain = Integer.parseInt(newsItem.substring(newsItem.indexOf("razed"), newsItem.indexOf("acres"))
						.replaceAll("[^0-9]", ""));
				  String target = newsItem.substring(newsItem.indexOf("acres of ") + 9).trim();
				  target = target.substring(0, target.indexOf('(')).trim();
				  out.addEnemyHit("Raze", attacker, target, warStarted ? 0 : gain, item.amount, warStarted);
			   } else if (item.news.matches(".*We have declared WAR on .*" + kdMatch + "[ ]{0,}")) {
				  warStarted = true;
			   } else if (item.news.matches(".*" + kdMatch + " has declared WAR with our kingdom![ ]{0,}")) {
				  warStarted = true;
			   } else if (item.news.matches(".*Unable to achieve victory, our Kingdom has withdrawn from war with .*" + kdMatch
					 + "\\. " + "Our failed war has finally ended![ ]{0,}")) {
				  if (war) {
					 break;
				  }
				  warStarted = false;
			   } else if (item.news.matches(".*" + kdMatch
					 + " has withdrawn from war\\. Our people rejoice at our victory![ ]{0,}")) {
				  if (war) {
					 break;
				  }
				  warStarted = false;
			   } else if (item.news.matches(".*We have accepted an offer of Peace by .*" + kdMatch + "\\. "
					 + "The people celebrate the end of our War![ ]{0,}")) {
				  if (war) {
					 break;
				  }
				  warStarted = false;
			   } else if (item.news.matches(".*" + kdMatch + " has accepted our offer of Peace\\. "
					 + "The people celebrate the end of our War![ ]{0,}")) {
				  if (war) {
					 break;
				  }
				  warStarted = false;
			   }
			}
		 }
	  } catch (IndexOutOfBoundsException e) {
		 Logger.getLogger("Error").error("", e);
	  } catch (PatternSyntaxException e) {
		 Logger.getLogger("Error").error("", e);
	  } catch (NumberFormatException e) {
		 Logger.getLogger("Error").error("", e);
	  }
	  return out;
   }

   /**
    * Determines if the supplied String is a news item Will return false if the bot isn't currently expecting anyone to paste news
    */
   public static boolean isNews(final String message) {
	  if (message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " captured " + AMOUNTREGEX
			+ " acres of land from .*" + KDREGEX + "\\.[ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " invaded .*" + KDREGEX + " and captured "
				  + AMOUNTREGEX + " acres of land\\.[ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + ", captured " + AMOUNTREGEX
				  + " acres of land from .*" + KDREGEX + "\\.[ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " invaded and pillaged .*" + KDREGEX
				  + "\\.[ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " attacked and pillaged the lands of .*"
				  + KDREGEX + "\\.[ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " invaded and stole from .*" + KDREGEX
				  + "\\.[ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " attacked and stole from .*" + KDREGEX
				  + "\\.[ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " attempted to invade .*" + KDREGEX
				  + "\\.[ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " attempted an invasion of .*" + KDREGEX
				  + ", but was repelled\\.[ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " recaptured " + AMOUNTREGEX
				  + " acres of land from .*" + KDREGEX + "\\.[ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " ambushed armies from .*" + KDREGEX
				  + " and took " + AMOUNTREGEX + " acres of land\\.[ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " killed " + AMOUNTREGEX + " people within .*"
				  + KDREGEX + "\\.[ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " invaded .*" + KDREGEX + " and killed "
				  + AMOUNTREGEX + " people\\.[ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " razed " + AMOUNTREGEX + " acres of .*"
				  + KDREGEX + "\\.[ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " invaded .*" + KDREGEX + " and razed "
				  + AMOUNTREGEX + " acres of land\\.[ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}We have declared WAR on .*" + KDREGEX + "![ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " has declared WAR with our kingdom![ ]{0,}")
			|| message.matches(".*" + DATEREGEX
				  + "[ ]{0,}\t[ ]{0,}Unable to achieve victory, our Kingdom has withdrawn from war with .*" + KDREGEX + "\\. "
				  + "Our failed war has finally ended![ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX
				  + " has withdrawn from war\\. Our people rejoice at our victory![ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}We have accepted an offer of Peace by .*" + KDREGEX + "\\. "
				  + "The people celebrate the end of our War![ ]{0,}")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " has accepted our offer of Peace\\. "
				  + "The people celebrate the end of our War![ ]{0,}")
			|| message.matches(".*" + DATEREGEX
				  + "[ ]{0,}\t[ ]{0,}Staying in the darkest shadows, .* slips out of .* unnoticed, never to be seen again\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}We have entered into a formal ceasefire with .*" + KDREGEX
				  + "\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX
				  + " has proposed a formal ceasefire with our kingdom\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.* has defected to .*" + KDREGEX + "")
			|| message.matches(".*" + DATEREGEX
				  + "[ ]{0,}\t[ ]{0,}Alas, the once proud province of .* has collapsed and lies in ruins\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}As the ultimate betrayal, .* destroys all in the land of .* "
				  + "before leaving for a new kingdom\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}We have proposed a ceasefire offer to .*" + KDREGEX + "\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " has accepted our ceasefire proposal!")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}Our kingdom has begun a .* Dragon project targetted at .*"
				  + KDREGEX + "\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX + " has begun a .* Dragon project against us!")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}Our dragon has set flight to ravage .*" + KDREGEX + "")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}A .* Dragon from .*" + KDREGEX
				  + " has begun ravaging our lands!")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.* has slain the dragon ravaging our lands!")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}Our kingdom has cancelled the dragon project to .*" + KDREGEX
				  + "\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.*" + KDREGEX
				  + " has cancelled their dragon project targetted at us\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}.* has sent an aid shipment to .*\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}The province of .* has defected to us from .*" + KDREGEX
				  + "\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}Alas, the truant .* has lead .* into a state of neglect\\. "
				  + ".* has now collapsed and lies in ruins\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}The leader of .* has chosen to join .*" + KDREGEX
				  + "\\. All in .* gather their possessions and depart this kingdom forever\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}In intra-kingdom war .*" + KDREGEX + " attempted to invade .*"
				  + KDREGEX + ", but failed\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}In local kingdom strife .*" + KDREGEX + " invaded  .*"
				  + KDREGEX + " and razed [0-9,]{1,} acres of land\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}In local kingdom strife, .*" + KDREGEX + " invaded .*"
				  + KDREGEX + " and captured [0-9,]{1,} acres of land\\.")
			|| message.matches(".*" + DATEREGEX + "[ ]{0,}\t[ ]{0,}The leader of .* has wisely chosen to join us from .*"
				  + KDREGEX + "\\."))
		 return true;
	  return false;
   }

   /**
    * Takes the unformatted news and parses them, turning them into NewsItem objects
    * 
    * @param pastedNews
    * @return
    */
   public static List<NewsItem> parsePastedNews(final List<String> pastedNews) {
	  if (pastedNews.isEmpty())
		 return new LinkedList<NewsItem>();
	  List<NewsItem> news = new LinkedList<NewsItem>();
	  Pattern pattern = Pattern.compile(".*?(" + DATEREGEX + ").*?\t(.*)");
	  Matcher matcher = null;
	  for (final String s : pastedNews) {
		 matcher = pattern.matcher(s);
		 if (matcher.matches()) {
			NewsItem newItem = new NewsItem(matcher.group(1).trim(), matcher.group(2).trim(), 1);
			if (news.contains(newItem)) {
			   ++news.get(news.indexOf(newItem)).amount;
			} else {
			   news.add(newItem);
			}
		 }
	  }

	  Collections.sort(news);

	  for (final NewsItem ni : NewsItem.getNews(news.get(0).date, "", false)) {
		 for (int i = 0; i < ni.amount; ++i) {
			if (news.contains(ni)) {
			   --news.get(news.indexOf(ni)).amount;
			}
		 }
	  }

	  List<NewsItem> out = new LinkedList<NewsItem>();
	  for (final NewsItem ni : news) {
		 if (ni.amount > 0) {
			out.add(ni);
		 }
	  }

	  return out;
   }

   private final List<String> pastedNews;
   private static final String DATEREGEX = "(?:January|February|March"
		 + "|April|May|June|July) (?:(?:[0-1][0-9])|(?:[2][0-4])|(?:[1-9])) of YR[0-9]{1,2}";

   private static final String AMOUNTREGEX = "[0-9,]{1,}";

   private static final String KDREGEX = "\\([0-9]{1,2}(:)[0-9]{1,2}\\)";

   private static boolean listenForNews = false;

   private static String newsPoster = "";

   public static final NewsHandler getInstance() {
	  if (INSTANCE == null) {
		 INSTANCE = new NewsHandler();
	  }
	  return INSTANCE;
   }

   public static final NewsHandler newInstance() {
	  INSTANCE = null;
	  return getInstance();
   }

   private NewsHandler() {
	  pastedNews = new ArrayList<String>();
   }

   @Override
   public void handle(final IRCCommand command, final IRCOutput output) {
   }

   public synchronized void handleAddnews(final IRCCommand command, final IRCOutput out) {
	  if (listenForNews && !newsPoster.equals(UserCentral.getInstance().getOnlineUser(command.getSender()))) {
		 out.addOutput(newsPoster + " is already adding news.");
	  } else {
		 if (command.size() == 1 && !listenForNews) {
			listenForNews = true;
			newsPoster = UserCentral.getInstance().getOnlineUser(command.getSender());
			out.addOutput(Colors.RED + "Add news by pasting them here and then calling !addnews done "
				  + "(or wait 30 seconds and it will happen automatically)");
			out.addOutput(Colors.RED + "Remember that it takes some time for the bot to get everything if you're pasting a lot");
			out.addOutput(Colors.RED + "The last few added news items:");
			List<NewsItem> lastAdded = NewsItem.getNews("", "", true);
			Collections.reverse(lastAdded);
			for (final NewsItem ni : lastAdded) {
			   for (int i = 0; i < ni.amount; ++i) {
				  out.addOutput(ni.date + " " + ni.news);
			   }
			}
			Bot.getInstance().getTimerHandler().setNewsTimer(new NewsTimer(command.getSender()));
		 } else if (command.size() == 2 && "done".equalsIgnoreCase(command.get(1)) && listenForNews) {
			Bot.getInstance().getTimerHandler().removeNewsTimer();
			if (!NewsItem.saveBatch(parsePastedNews(pastedNews)))
			   out.addOutput("Failed at saving the news to the database");
			pastedNews.clear();
			out.addOutput("News adding and timer stopped");
			listenForNews = false;
			newsPoster = "";
		 }
	  }
   }

   public void handleMintime(final IRCCommand command, final IRCOutput out) {
	  String lastWarStart = getLastWarInfo("", true);
	  if ("".equals(lastWarStart)) {
		 out.addOutput("The war start has not been added to the news");
	  } else {
		 UtopiaTime ut;
		 try {
			ut = new UtopiaTime(lastWarStart);
			String minTime = ut.getFutureDate(48.0);
			Long minTimeLong = new UtopiaTime(minTime).getTime();
			if (minTimeLong < GregorianCalendar.getInstance().getTimeInMillis()) {
			   out.addOutput("Min time was: " + minTime + ", " + UtopiaTime.compareTimes(minTimeLong) + " ago");
			} else {
			   out.addOutput("Min time is: " + minTime + ", in " + UtopiaTime.compareTimes(minTimeLong));
			}
		 } catch (UtoDateException e) {
		 }
	  }
   }

   public void handleSummary(final IRCCommand command, final IRCOutput out) {
	  if (command.size() >= 8 && command.validate(1, InputType.KDLOC) && command.validate(2, 5, InputType.UTODATE)
			&& command.validate(5, 8, InputType.UTODATE)) {
		 // for a kd from one date to another
		 if (command.size() > 8) {
			String user = CommonMethods.getUserFromDB(command.get(8), false);
			String prov = command.getPartialMessage(8, command.size());
			if (!"".equals(user)) {
			   prov = CommonMethods.getProv(user);
			}
			out.addOutput(Colors.RED + "Summary - " + ("".equals(user) ? prov : user));
			out.addOutput(getSummary(
				  false,
				  NewsItem.getNews(command.getPartialMessage(2, 5).replaceAll("(th,|rd,|nd,|st,)", " of"), command
						.getPartialMessage(5, 8).replaceAll("(th,|rd,|nd,|st,)", " of"), false), command.get(1)).getIRCOutput(
				  Constants.SUMMARY_PROVINCE, prov));
		 } else {
			out.addOutput(Colors.RED + "Summary - (" + Main.getBotSettings().getKdLoc() + ") vs. " + command.get(1));
			out.addOutput(getSummary(
				  false,
				  NewsItem.getNews(command.getPartialMessage(2, 5).replaceAll("(th,|rd,|nd,|st,)", " of"), command
						.getPartialMessage(5, 8).replaceAll("(th,|rd,|nd,|st,)", " of"), false), command.get(1)).getIRCOutput(
				  Constants.SUMMARY_KD));
		 }
	  } else if (command.size() >= 7 && command.validate(1, 4, InputType.UTODATE) && command.validate(4, 7, InputType.UTODATE)) {
		 // for all from one date to another
		 if (command.size() > 7) {
			String user = CommonMethods.getUserFromDB(command.get(7), false);
			String prov = command.getPartialMessage(7, command.size());
			if (!"".equals(user)) {
			   prov = CommonMethods.getProv(user);
			}
			out.addOutput(Colors.RED + "Summary - " + ("".equals(user) ? prov : user));
			out.addOutput(getSummary(
				  false,
				  NewsItem.getNews(command.getPartialMessage(1, 4).replaceAll("(th,|rd,|nd,|st,)", " of"), command
						.getPartialMessage(4, 7).replaceAll("(th,|rd,|nd,|st,)", " of"), false), "").getIRCOutput(
				  Constants.SUMMARY_PROVINCE, prov));
		 } else {
			out.addOutput(Colors.RED + "Summary - (" + Main.getBotSettings().getKdLoc() + ") vs. Utopia");
			out.addOutput(getSummary(
				  false,
				  NewsItem.getNews(command.getPartialMessage(1, 4).replaceAll("(th,|rd,|nd,|st,)", " of"), command
						.getPartialMessage(4, 7).replaceAll("(th,|rd,|nd,|st,)", " of"), false), "").getIRCOutput(
				  Constants.SUMMARY_KD));
		 }
	  } else if (command.size() >= 4 && command.validate(1, 4, InputType.UTODATE)) {
		 // for all from one date
		 if (command.size() > 4) {
			String user = CommonMethods.getUserFromDB(command.get(4), false);
			String prov = command.getPartialMessage(4, command.size());
			if (!"".equals(user)) {
			   prov = CommonMethods.getProv(user);
			}
			out.addOutput(Colors.RED + "Summary - " + ("".equals(user) ? prov : user));
			out.addOutput(getSummary(false,
				  NewsItem.getNews(command.getPartialMessage(1, 4).replaceAll("(th,|rd,|nd,|st,)", " of"), "", false), "")
				  .getIRCOutput(Constants.SUMMARY_PROVINCE, prov));
		 } else {
			out.addOutput(Colors.RED + "Summary - (" + Main.getBotSettings().getKdLoc() + ") vs. Utopia");
			out.addOutput(getSummary(false,
				  NewsItem.getNews(command.getPartialMessage(1, 4).replaceAll("(th,|rd,|nd,|st,)", " of"), "", false), "")
				  .getIRCOutput(Constants.SUMMARY_KD));
		 }
	  } else if (command.size() >= 5 && command.validate(1, InputType.KDLOC) && command.validate(2, 5, InputType.UTODATE)) {
		 // for a kd from the given date
		 if (command.size() > 5) {
			String user = CommonMethods.getUserFromDB(command.get(5), false);
			String prov = command.getPartialMessage(5, command.size());
			if (!"".equals(user)) {
			   prov = CommonMethods.getProv(user);
			}
			out.addOutput(Colors.RED + "Summary - " + ("".equals(user) ? prov : user));
			out.addOutput(getSummary(false,
				  NewsItem.getNews(command.getPartialMessage(2, 5).replaceAll("(th,|rd,|nd,|st,)", " of"), "", false),
				  command.get(1)).getIRCOutput(Constants.SUMMARY_PROVINCE, prov));
		 } else {
			out.addOutput(Colors.RED + "Summary - (" + Main.getBotSettings().getKdLoc() + ") vs. " + command.get(1));
			out.addOutput(getSummary(false,
				  NewsItem.getNews(command.getPartialMessage(2, 5).replaceAll("(th,|rd,|nd,|st,)", " of"), "", false),
				  command.get(1)).getIRCOutput(Constants.SUMMARY_KD));
		 }
	  } else if (command.size() >= 2 && command.validate(1, InputType.KDLOC)) {
		 // for a kd
		 if (command.size() > 2) {
			String user = CommonMethods.getUserFromDB(command.get(2), false);
			String prov = command.getPartialMessage(2, command.size());
			if (!"".equals(user)) {
			   prov = CommonMethods.getProv(user);
			}
			out.addOutput(Colors.RED + "Summary - " + ("".equals(user) ? prov : user));
			out.addOutput(getSummary(false, NewsItem.getNews("", "", false), command.get(1)).getIRCOutput(
				  Constants.SUMMARY_PROVINCE, prov));
		 } else {
			out.addOutput(Colors.RED + "Summary - (" + Main.getBotSettings().getKdLoc() + ") vs. " + command.get(1));
			out.addOutput(getSummary(false, NewsItem.getNews("", "", false), command.get(1)).getIRCOutput(Constants.SUMMARY_KD));
		 }
	  }
	  if (!out.getOutput().isEmpty()) {
		 List<NewsItem> lastAdded = NewsItem.getNews("", "", true);
		 if (!lastAdded.isEmpty()) {
			out.addOutput(Colors.OLIVE + "Last added: " + Colors.NORMAL + lastAdded.get(0).date + " " + lastAdded.get(0).news);
		 }
	  }
   }

   public boolean handleText(final String message, final String channel, final String sender, final IRCOutput out) {
	  pastedNews.add(message);
	  Bot.getInstance().getTimerHandler().setNewsTimer(new NewsTimer(sender));
	  return true;
   }

   public void handleWar(final IRCCommand command, final IRCOutput out) {
	  if (command.size() >= 2 && command.validate(1, InputType.KDLOC)) {
		 String lastWarStart = getLastWarInfo(command.get(1), true);
		 String lastWarEnd = getLastWarInfo(command.get(1), false);
		 if ("".equals(lastWarStart)) {
			out.addOutput("The war start has not been added to the news");
		 } else {
			if (command.size() == 2 && command.validate(1, InputType.KDLOC)) {
			   out.addOutput(Colors.RED + "War summary - (" + Main.getBotSettings().getKdLoc() + ") vs. " + command.get(1));
			   List<NewsItem> newsList = NewsItem.getNews(lastWarStart, lastWarEnd, false);
			   out.addOutput(getSummary(true, newsList, command.get(1)).getIRCOutput(Constants.SUMMARY_KD));
			   if (!out.getOutput().isEmpty() && "".equals(lastWarEnd)) {
				  List<NewsItem> lastAdded = NewsItem.getNews("", "", true);
				  if (!lastAdded.isEmpty()) {
					 out.addOutput(Colors.OLIVE + "Last added: " + Colors.NORMAL + lastAdded.get(0).date + " "
						   + lastAdded.get(0).news);
				  }
			   } else {
				  out.addOutput(Colors.OLIVE + "Last added: " + Colors.NORMAL + ((LinkedList<NewsItem>) newsList).getLast().date
						+ " " + ((LinkedList<NewsItem>) newsList).getLast().news);
			   }
			} else if (command.size() >= 3 && command.validate(1, InputType.KDLOC)) {
			   String user = CommonMethods.getUserFromDB(command.get(2), false);
			   String prov = command.getPartialMessage(2, command.size());
			   if (!"".equals(user)) {
				  prov = CommonMethods.getProv(user);
			   }
			   out.addOutput(Colors.RED + "Summary - " + ("".equals(user) ? prov : user));
			   out.addOutput(getSummary(true, NewsItem.getNews(lastWarStart, lastWarEnd, false), command.get(1)).getIRCOutput(
					 Constants.SUMMARY_PROVINCE, prov));
			}
		 }
	  }
   }

   @Override
   public boolean willHandleText(final String message, final String channel, final String sender) {
	  return listenForNews && newsPoster.equals(UserCentral.getInstance().getOnlineUser(sender)) && isNews(message);
   }
}